The code below reproduces key computations from the paper " Regulation of fish stocks without stock-recruitment relationships: the case of small pelagic fish" by Mariella Canales, Gustav Delius and Richard Law, using the R package mizer to implement the size-spectrum model. This notebook should be used after reading that paper.

Setting up the model

We set up the model according to the description in the paper in Appendix A, with the parameters from Appendix B, but without diffusion (term (e) in equation (A.1)).

Parameters

We create a list holding the model parameters

p <- list(
    dt = 0.001,
    dx = 0.1,
    w_min = 0.0003,
    w_inf = 66.5,
    ppmr_min = 100,
    ppmr_max = 30000,
    gamma = 750,
    alpha = 0.85, # q
    K = 0.1, # alpha
    # Larval mortality
    mu_l = 0,
    w_l = 0.03,
    rho_l = 5,
    # background mortality
    mu_0 = 1,
    rho_b = -0.25,
    # Senescent mortality
    w_s = 0.5,
    rho_s = 1,
    # reproduction
    w_mat = 10,
    rho_m = 15,
    rho_inf = 0.2,
    epsilon_R = 0.1,
    # plankton
    w_pp_cutoff = 0.1,
    r0 = 10,
    a0 = 100,
    i0 = 100,
    rho = 0.85,
    lambda = 2
)

Mortality

We define a function for setting the background and larval mortality as described in equations (A.5) and (A.6).

setAnchovyMort <- 
  function(params, p) {
    mu_b <- rep(0, length(params@w))
    mu_b[params@w <= p$w_s] <- 
      (p$mu_0 * (params@w / p$w_min)^p$rho_b)[params@w < p$w_s]
    if (p$mu_0 > 0) {
      mu_s <- min(mu_b[params@w <= p$w_s])
    } else {
      mu_s <- p$mu_s
    }
    mu_b[params@w >= p$w_s] <- 
      (mu_s * (params@w / p$w_s)^p$rho_s)[params@w >= p$w_s]
    # Add larval mortality
    mu_b <- mu_b + p$mu_l / (1 + (params@w / p$w_l)^p$rho_l)
    
    params@mu_b[] <- mu_b
    return(params)
  }

Plankton dynamics

To prepare for random changes in plankton carrying capacity every half year, we create an environment to maintain state between function calls.

plankton_state <- new.env(parent = emptyenv())
plankton_state$time <- 0
plankton_state$factor <- 1
plankton_state$random <- FALSE

We implement the logistic plankton dynamics with immigration, as described in equation (A.11)

plankton_logistic <- function(params, 
                              n = params@initial_n, 
                              n_pp = params@initial_n_pp, 
                              B = params@initial_B, 
                              rates = getRates(params), 
                              dt = 0.1, ...) {
    plankton_state$time <- plankton_state$time + dt
    if (plankton_state$random && plankton_state$time >= 0.5) {
        plankton_state$factor <- exp(runif(1, log(1/2), log(2)))
        plankton_state$time <- 0
    }
    f <- params@rr_pp * n_pp * (1 - n_pp / params@cc_pp / plankton_state$factor) + 
        i - rates$plankton_mort * n_pp 
    f[is.na(f)] <- 0
    return(n_pp + dt * f)
}

Feeding kernel

We define the feeding kernel described in equation (A.2)

norm_box_pred_kernel <- function(ppmr, ppmr_min, ppmr_max) {
    phi <- rep(1, length(ppmr))
    phi[ppmr > ppmr_max] <- 0
    phi[ppmr < ppmr_min] <- 0
    # Do not allow feeding at own size
    phi[1] <- 0
    # normalise in log space
    logppmr <- log(ppmr)
    dl <- logppmr[2] - logppmr[1]
    N <- sum(phi) * dl
    phi <- phi / N
    return(phi)
}

Set model

We are now ready to set up the MizerParams object describing the Anchovy - Plankton model from the paper:

setModel <- function(p) {
  kappa = p$a0 * exp(-6.9*(p$lambda - 1))
  n = 2/3 # irrelevant value
  
  species_params <- data.frame(
    species = "Anchovy",
    w_min = p$w_min,
    w_mat = p$w_mat,
    m = p$rho_inf + n,
    w_inf = p$w_inf,
    erepro = p$epsilon_R,
    alpha = p$K,
    ks = 0,
    gamma = p$gamma,
    ppmr_min = p$ppmr_min,
    ppmr_max = p$ppmr_max,
    pred_kernel_type = "norm_box",
    h = Inf,
    R_max = Inf,
    linecolour = "brown")
  
  no_w <- round(log(p$w_inf / p$w_min) / p$dx)
  
  params <- set_multispecies_model(
    no_w = no_w,
    species_params,
    lambda = p$lambda,
    kappa = kappa,
    w_pp_cutoff = p$w_pp_cutoff,
    q = p$alpha,
    plankton_dynamics = plankton_logistic)

  params@rr_pp[] <- p$r0 * params@w_full^(p$rho - 1)
  return(params)
}

params <- setModel(p)
i <- p$i0 * params@w_full^(-params@lambda) * exp(-6.9*(params@lambda - 1))

Without larval mortality or cannibalism

We first run the model without larval mortality and without cannibalism

p$mu_l <- 0
params <- setAnchovyMort(params, p)
params@interaction[] <- 0

We set an initial abundance and run for 10 years.

params@initial_n[] <- 0.001 * params@w^(-1.8)
params@initial_n_pp[] <- params@cc_pp
sim <- project(params, t_max = 10, dt = p$dt, progress_bar = FALSE)

At this point we reduce the anchovy abundance by an overall factor of 10^7 and then run the simulation for a further 30 years.

sim@n[11, , ] <- sim@n[11, , ] / 10^7
sim <- project(sim, t_max = 30, dt = p$dt, t_save = 0.2, progress_bar = FALSE)

Figure 2a

Plotting the spectra at year 30 gives Figure 2a. Here we plot the plankton spectrum and the anchovy spectrum using the same y-axis. Figure 2a in the paper uses different axes.

plotSpectra(sim, power = 2, wlim = c(1e-8, NA), ylim = c(1e-5, NA),
            time_range = 30)

This does not look exactly the same as the corresponding graph in the paper because the pile-up is not smoothed by diffusion, but it displays the same qualitative behaviour.

Figure 2b

Figure 2b plots the death rate on the anchovy as a function of anchovy body size.

t <- as.numeric(dimnames(sim@n)$time) == 30
mort <- getMort(params, n = sim@n[t, , ],
                n_pp = sim@n_pp[t, ], effort = 0)
mort <- melt(mort)
plot_ly(mort) %>% 
    add_lines(x = ~w_prey, y = ~value) %>% 
    layout(p, xaxis = list(type = "log", exponentformat = "power",
                           title_text = "body mass (g)"),
           yaxis = list(title_text = "death rate (1/year)"))

Figure 2c

abm <- melt(getBiomass(sim))
pbm <- sim@n_pp %*% (params@w_full * params@dw_full)
pbm <- melt(pbm)
pbm$Var2 <- NULL
pbm$sp = "Plankton"
bm <- rbind(pbm, abm)
plot_ly(bm) %>% 
    filter(time >= 10) %>% 
    add_lines(x = ~time, y = ~value, color = ~sp) %>% 
    # Use logarithmic axes
    layout(p, yaxis = list(type = "log", exponentformat = "power",
                           title_text = "biomass (g/m^3)"),
           xaxis = list(title_text = "time (year)"))

With cannibalism

Turn on cannibalism

params@interaction[] <- 1

We set an initial abundance and run for 10 years.

params@initial_n[] <- 0.001 * params@w^(-1.8)
params@initial_n_pp[] <- params@cc_pp
simc <- project(params, t_max = 10, dt = p$dt, progress_bar = FALSE)

At this point we reduce the anchovy abundance by an overall factor of 10^7 and then run the simulation for a further 30 years.

simc@n[11, , ] <- simc@n[11, , ] / 10^7
simc <- project(simc, t_max = 30, dt = p$dt, t_save = 0.2, progress_bar = FALSE)

Figure 2d

While Figure 2d shows the background death and the larval death separately, here for simplicity we plot only their sum.

t <- as.numeric(dimnames(simc@n)$time) == 36.8
mort <- getMort(params, n = simc@n[t, , ],
                n_pp = simc@n_pp[t, ], effort = 0)
mort <- melt(mort)
plot_ly(mort) %>% 
    add_lines(x = ~w_prey, y = ~value) %>% 
    layout(p, xaxis = list(type = "log", exponentformat = "power",
                           title_text = "body mass (g)"),
           yaxis = list(title_text = "death rate (1/year)"))

We made the plot for time = 36.8 years because the oscillations of the spectrum are shifted with respect to those in the paper, as the following figure shows.

Figure 2e

abm <- melt(getBiomass(simc))
abmr <- melt(getBiomass(simc, min_w = 0.01, max_w = 0.4))
abmr$sp = "small Anchovy"
pbm <- simc@n_pp %*% (params@w_full * params@dw_full)
pbm <- melt(pbm)
pbm$Var2 <- NULL
pbm$sp = "Plankton"
bm <- rbind(pbm, abm, abmr)
plot_ly(bm) %>% 
    filter(time >= 10) %>% 
    add_lines(x = ~time, y = ~value, color = ~sp) %>% 
    # Use logarithmic axes
    layout(p, yaxis = list(type = "log", exponentformat = "power",
                           title_text = "biomass (g/m^3)",
                           range = c(-7, 2)),
           xaxis = list(title_text = "time (year)"))

Animation

Here is an animation showing the evolution of the spectra from year 26 to year 40.

nf <- melt(simc@n)
n_ppf <- melt(simc@n_pp)
n_ppf$sp <- "Plankton"
nf <- rbind(nf, n_ppf)

plot_ly(nf) %>%
    # show only part of plankton spectrum
    filter(w > 10^-5) %>% 
    # start at time 20
    filter(time >= 26) %>% 
    # calculate biomass density with respect to log size
    mutate(b = value * w^2) %>% 
    # Plot lines
    add_lines(
        x = ~w, y = ~b,
        color = ~sp,
        frame = ~time,
        line = list(simplify = FALSE)
    ) %>% 
    # Use logarithmic axes
    layout(p, xaxis = list(type = "log", exponentformat = "power",
                           title_text = "body mass (g)"),
           yaxis = list(type = "log", exponentformat = "power",
                        title_text = "biomass (g/m^3)",
                        range = c(-8, 0)))

With larval mortality

Turn on larval mortality

p$mu_l <- 21
params <- setAnchovyMort(params, p)

We set an initial abundance and run for 10 years.

params@initial_n[] <- 0.001 * params@w^(-1.8)
params@initial_n_pp[] <- params@cc_pp
siml <- project(params, t_max = 10, dt = p$dt, progress_bar = FALSE)

At this point we reduce the anchovy abundance by an overall factor of 10^7 and then run the simulation for a further 30 years.

siml@n[11, , ] <- siml@n[11, , ] / 10^7
siml <- project(siml, t_max = 30, dt = p$dt, t_save = 0.2, progress_bar = FALSE)

Figure 2f

I have not yet split the mortality up into its causes in the following figure. But overall it looks right.

t <- as.numeric(dimnames(siml@n)$time) == 30
mort <- getMort(params, n = siml@n[t, , ],
                n_pp = siml@n_pp[t, ], effort = 0)
mort <- melt(mort)
plot_ly(mort) %>% 
    add_lines(x = ~w_prey, y = ~value) %>% 
    layout(p, xaxis = list(type = "log", exponentformat = "power",
                           title_text = "body mass (g)"),
           yaxis = list(title_text = "death rate (1/year)"))

Figure 2g

abm <- melt(getBiomass(siml))
pbm <- siml@n_pp %*% (params@w_full * params@dw_full)
pbm <- melt(pbm)
pbm$Var2 <- NULL
pbm$sp = "Plankton"
bm <- rbind(abm, pbm)
plot_ly(bm) %>% 
    filter(time >= 10) %>% 
    add_lines(x = ~time, y = ~value, color = ~sp) %>% 
    # Use logarithmic axes
    layout(p, yaxis = list(type = "log", exponentformat = "power",
                           title_text = "biomass (g/m^3)",
                           range = c(-7, 2)),
           xaxis = list(title_text = "time (year)"))

Figure 3a

gcp <- plotGrowthCurves(siml, max_age = 4)
gcp + scale_y_continuous(trans = "log10")

Figure 3b

t_min_idx <- sum(as.numeric(dimnames(siml@n)$time) <= 15)
t_max_idx <- dim(siml@n)[1]
t_step_idx <- 1 / 0.2  # 1 year steps
ssb <- getSSB(siml)[seq(t_min_idx, t_max_idx - t_step_idx, t_step_idx)]
rec_idx <- sum(params@w < 10)
n_rec <- siml@n[seq(t_min_idx + t_step_idx, t_max_idx, t_step_idx), , rec_idx]
# Convert to density in log weight
n_rec <- n_rec * params@w[rec_idx]
plot(ssb, n_rec, type = "l", log = "xy",
     xlim = c(1e-5, 1e-1), ylim = c(1e-5, 1e-2))

Random plankton

Switch on randomness for plankton carrying capacity

plankton_state$random <- TRUE

Of course our figures will not look exactly like those in the paper because we will get a different randomisation, but they will be qualitatively the same.

We set an initial abundance and run for 10 years.

params@initial_n[] <- 0.001 * params@w^(-1.8)
params@initial_n_pp[] <- params@cc_pp
simr <- project(params, t_max = 10, dt = p$dt, progress_bar = FALSE)

At this point we reduce the anchovy abundance by an overall factor of 10^7 and then run the simulation for a further 30 years.

simr@n[11, , ] <- simr@n[11, , ] / 10^7
simr <- project(simr, t_max = 30, dt = p$dt, t_save = 0.2, progress_bar = FALSE)

Figure 4a

abm <- melt(getBiomass(simr))
pbm <- simr@n_pp %*% (params@w_full * params@dw_full)
pbm <- melt(pbm)
pbm$Var2 <- NULL
pbm$sp = "Plankton"
bm <- rbind(abm, pbm)
plot_ly(bm) %>% 
    filter(time >= 10) %>% 
    add_lines(x = ~time, y = ~value, color = ~sp) %>% 
    # Use logarithmic axes
    layout(p, yaxis = list(type = "log", exponentformat = "power",
                           title_text = "biomass (g/m^3)",
                           range = c(-3.2, 0.8)),
           xaxis = list(title_text = "time (year)"))

Figure 4b

t_min_idx <- sum(as.numeric(dimnames(siml@n)$time) <= 15)
t_max_idx <- dim(siml@n)[1]
t_step_idx <- 1 / 0.2  # 1 year steps
ssb <- getSSB(simr)[seq(t_min_idx, t_max_idx - t_step_idx, t_step_idx)]
rec_idx <- sum(params@w < 10)
n_rec <- simr@n[seq(t_min_idx + t_step_idx, t_max_idx, t_step_idx), , rec_idx]
# Convert to density in log weight
n_rec <- n_rec * params@w[rec_idx]
plot(ssb, n_rec, log = "xy", ylim = c(1e-5, 1e-1), pch = 20,
     xlab = "spawning stock biomass (g/m^3)", xlim = c(1e-3, 1),
     ylab = "density of recruits (1/m^3)")

LS0tCnRpdGxlOiAiUmVndWxhdGlvbiBvZiBmaXNoIHN0b2NrcyB3aXRob3V0IHN0b2NrLXJlY3J1aXRtZW50IHJlbGF0aW9uc2hpcHM6IHRoZSBjYXNlIG9mIHNtYWxsIHBlbGFnaWMgZmlzaCIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCmZpZ19oZWlnaHQ6IDQKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpsaWJyYXJ5KG1pemVyKQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KG1hZ3JpdHRyKQpsaWJyYXJ5KHBsb3RseSkKYGBgCgpUaGUgY29kZSBiZWxvdyByZXByb2R1Y2VzIGtleSBjb21wdXRhdGlvbnMgZnJvbSB0aGUgcGFwZXIgIgpSZWd1bGF0aW9uIG9mIGZpc2ggc3RvY2tzIHdpdGhvdXQgc3RvY2stcmVjcnVpdG1lbnQgcmVsYXRpb25zaGlwczogdGhlIGNhc2Ugb2YgCnNtYWxsIHBlbGFnaWMgZmlzaCIgYnkgTWFyaWVsbGEgQ2FuYWxlcywgR3VzdGF2IERlbGl1cyBhbmQgUmljaGFyZCBMYXcsIHVzaW5nIAp0aGUgUiBwYWNrYWdlIG1pemVyIHRvIGltcGxlbWVudCB0aGUgc2l6ZS1zcGVjdHJ1bSBtb2RlbC4gVGhpcyBub3RlYm9vayBzaG91bGQgCmJlIHVzZWQgYWZ0ZXIgcmVhZGluZyB0aGF0IHBhcGVyLiAKCiMgU2V0dGluZyB1cCB0aGUgbW9kZWwKV2Ugc2V0IHVwIHRoZSBtb2RlbCBhY2NvcmRpbmcgdG8gdGhlIGRlc2NyaXB0aW9uIGluIHRoZSBwYXBlciBpbiBBcHBlbmRpeCBBLAp3aXRoIHRoZSBwYXJhbWV0ZXJzIGZyb20gQXBwZW5kaXggQiwgYnV0IHdpdGhvdXQgZGlmZnVzaW9uICh0ZXJtIChlKSBpbiAKZXF1YXRpb24gKEEuMSkpLiAKCiMjIFBhcmFtZXRlcnMKV2UgY3JlYXRlIGEgbGlzdCBob2xkaW5nIHRoZSBtb2RlbCBwYXJhbWV0ZXJzCmBgYHtyfQpwIDwtIGxpc3QoCiAgICBkdCA9IDAuMDAxLAogICAgZHggPSAwLjEsCiAgICB3X21pbiA9IDAuMDAwMywKICAgIHdfaW5mID0gNjYuNSwKICAgIHBwbXJfbWluID0gMTAwLAogICAgcHBtcl9tYXggPSAzMDAwMCwKICAgIGdhbW1hID0gNzUwLAogICAgYWxwaGEgPSAwLjg1LCAjIHEKICAgIEsgPSAwLjEsICMgYWxwaGEKICAgICMgTGFydmFsIG1vcnRhbGl0eQogICAgbXVfbCA9IDAsCiAgICB3X2wgPSAwLjAzLAogICAgcmhvX2wgPSA1LAogICAgIyBiYWNrZ3JvdW5kIG1vcnRhbGl0eQogICAgbXVfMCA9IDEsCiAgICByaG9fYiA9IC0wLjI1LAogICAgIyBTZW5lc2NlbnQgbW9ydGFsaXR5CiAgICB3X3MgPSAwLjUsCiAgICByaG9fcyA9IDEsCiAgICAjIHJlcHJvZHVjdGlvbgogICAgd19tYXQgPSAxMCwKICAgIHJob19tID0gMTUsCiAgICByaG9faW5mID0gMC4yLAogICAgZXBzaWxvbl9SID0gMC4xLAogICAgIyBwbGFua3RvbgogICAgd19wcF9jdXRvZmYgPSAwLjEsCiAgICByMCA9IDEwLAogICAgYTAgPSAxMDAsCiAgICBpMCA9IDEwMCwKICAgIHJobyA9IDAuODUsCiAgICBsYW1iZGEgPSAyCikKYGBgCgojIyBNb3J0YWxpdHkKV2UgZGVmaW5lIGEgZnVuY3Rpb24gZm9yIHNldHRpbmcgdGhlIGJhY2tncm91bmQgYW5kIGxhcnZhbCBtb3J0YWxpdHkgYXMgCmRlc2NyaWJlZCBpbiBlcXVhdGlvbnMgKEEuNSkgYW5kIChBLjYpLgpgYGB7cn0Kc2V0QW5jaG92eU1vcnQgPC0gCiAgZnVuY3Rpb24ocGFyYW1zLCBwKSB7CiAgICBtdV9iIDwtIHJlcCgwLCBsZW5ndGgocGFyYW1zQHcpKQogICAgbXVfYltwYXJhbXNAdyA8PSBwJHdfc10gPC0gCiAgICAgIChwJG11XzAgKiAocGFyYW1zQHcgLyBwJHdfbWluKV5wJHJob19iKVtwYXJhbXNAdyA8IHAkd19zXQogICAgaWYgKHAkbXVfMCA+IDApIHsKICAgICAgbXVfcyA8LSBtaW4obXVfYltwYXJhbXNAdyA8PSBwJHdfc10pCiAgICB9IGVsc2UgewogICAgICBtdV9zIDwtIHAkbXVfcwogICAgfQogICAgbXVfYltwYXJhbXNAdyA+PSBwJHdfc10gPC0gCiAgICAgIChtdV9zICogKHBhcmFtc0B3IC8gcCR3X3MpXnAkcmhvX3MpW3BhcmFtc0B3ID49IHAkd19zXQogICAgIyBBZGQgbGFydmFsIG1vcnRhbGl0eQogICAgbXVfYiA8LSBtdV9iICsgcCRtdV9sIC8gKDEgKyAocGFyYW1zQHcgLyBwJHdfbClecCRyaG9fbCkKICAgIAogICAgcGFyYW1zQG11X2JbXSA8LSBtdV9iCiAgICByZXR1cm4ocGFyYW1zKQogIH0KYGBgCgojIyBQbGFua3RvbiBkeW5hbWljcwpUbyBwcmVwYXJlIGZvciByYW5kb20gY2hhbmdlcyBpbiBwbGFua3RvbiBjYXJyeWluZyBjYXBhY2l0eSBldmVyeSBoYWxmIHllYXIsCndlIGNyZWF0ZSBhbiBlbnZpcm9ubWVudCB0byBtYWludGFpbiBzdGF0ZSBiZXR3ZWVuIGZ1bmN0aW9uIGNhbGxzLgpgYGB7cn0KcGxhbmt0b25fc3RhdGUgPC0gbmV3LmVudihwYXJlbnQgPSBlbXB0eWVudigpKQpwbGFua3Rvbl9zdGF0ZSR0aW1lIDwtIDAKcGxhbmt0b25fc3RhdGUkZmFjdG9yIDwtIDEKcGxhbmt0b25fc3RhdGUkcmFuZG9tIDwtIEZBTFNFCmBgYAoKV2UgaW1wbGVtZW50IHRoZSBsb2dpc3RpYyBwbGFua3RvbiBkeW5hbWljcyB3aXRoIGltbWlncmF0aW9uLCBhcyBkZXNjcmliZWQgaW4KZXF1YXRpb24gKEEuMTEpCmBgYHtyfQpwbGFua3Rvbl9sb2dpc3RpYyA8LSBmdW5jdGlvbihwYXJhbXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuID0gcGFyYW1zQGluaXRpYWxfbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fcHAgPSBwYXJhbXNAaW5pdGlhbF9uX3BwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQiA9IHBhcmFtc0Bpbml0aWFsX0IsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYXRlcyA9IGdldFJhdGVzKHBhcmFtcyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkdCA9IDAuMSwgLi4uKSB7CiAgICBwbGFua3Rvbl9zdGF0ZSR0aW1lIDwtIHBsYW5rdG9uX3N0YXRlJHRpbWUgKyBkdAogICAgaWYgKHBsYW5rdG9uX3N0YXRlJHJhbmRvbSAmJiBwbGFua3Rvbl9zdGF0ZSR0aW1lID49IDAuNSkgewogICAgICAgIHBsYW5rdG9uX3N0YXRlJGZhY3RvciA8LSBleHAocnVuaWYoMSwgbG9nKDEvMiksIGxvZygyKSkpCiAgICAgICAgcGxhbmt0b25fc3RhdGUkdGltZSA8LSAwCiAgICB9CiAgICBmIDwtIHBhcmFtc0Bycl9wcCAqIG5fcHAgKiAoMSAtIG5fcHAgLyBwYXJhbXNAY2NfcHAgLyBwbGFua3Rvbl9zdGF0ZSRmYWN0b3IpICsgCiAgICAgICAgaSAtIHJhdGVzJHBsYW5rdG9uX21vcnQgKiBuX3BwIAogICAgZltpcy5uYShmKV0gPC0gMAogICAgcmV0dXJuKG5fcHAgKyBkdCAqIGYpCn0KYGBgCgojIyBGZWVkaW5nIGtlcm5lbApXZSBkZWZpbmUgdGhlIGZlZWRpbmcga2VybmVsIGRlc2NyaWJlZCBpbiBlcXVhdGlvbiAoQS4yKQpgYGB7cn0Kbm9ybV9ib3hfcHJlZF9rZXJuZWwgPC0gZnVuY3Rpb24ocHBtciwgcHBtcl9taW4sIHBwbXJfbWF4KSB7CiAgICBwaGkgPC0gcmVwKDEsIGxlbmd0aChwcG1yKSkKICAgIHBoaVtwcG1yID4gcHBtcl9tYXhdIDwtIDAKICAgIHBoaVtwcG1yIDwgcHBtcl9taW5dIDwtIDAKICAgICMgRG8gbm90IGFsbG93IGZlZWRpbmcgYXQgb3duIHNpemUKICAgIHBoaVsxXSA8LSAwCiAgICAjIG5vcm1hbGlzZSBpbiBsb2cgc3BhY2UKICAgIGxvZ3BwbXIgPC0gbG9nKHBwbXIpCiAgICBkbCA8LSBsb2dwcG1yWzJdIC0gbG9ncHBtclsxXQogICAgTiA8LSBzdW0ocGhpKSAqIGRsCiAgICBwaGkgPC0gcGhpIC8gTgogICAgcmV0dXJuKHBoaSkKfQpgYGAKCiMjIFNldCBtb2RlbApXZSBhcmUgbm93IHJlYWR5IHRvIHNldCB1cCB0aGUgTWl6ZXJQYXJhbXMgb2JqZWN0IGRlc2NyaWJpbmcgdGhlIApBbmNob3Z5IC0gUGxhbmt0b24gbW9kZWwgZnJvbSB0aGUgcGFwZXI6CmBgYHtyIG1lc3NhZ2U9RkFMU0V9CnNldE1vZGVsIDwtIGZ1bmN0aW9uKHApIHsKICBrYXBwYSA9IHAkYTAgKiBleHAoLTYuOSoocCRsYW1iZGEgLSAxKSkKICBuID0gMi8zICMgaXJyZWxldmFudCB2YWx1ZQogIAogIHNwZWNpZXNfcGFyYW1zIDwtIGRhdGEuZnJhbWUoCiAgICBzcGVjaWVzID0gIkFuY2hvdnkiLAogICAgd19taW4gPSBwJHdfbWluLAogICAgd19tYXQgPSBwJHdfbWF0LAogICAgbSA9IHAkcmhvX2luZiArIG4sCiAgICB3X2luZiA9IHAkd19pbmYsCiAgICBlcmVwcm8gPSBwJGVwc2lsb25fUiwKICAgIGFscGhhID0gcCRLLAogICAga3MgPSAwLAogICAgZ2FtbWEgPSBwJGdhbW1hLAogICAgcHBtcl9taW4gPSBwJHBwbXJfbWluLAogICAgcHBtcl9tYXggPSBwJHBwbXJfbWF4LAogICAgcHJlZF9rZXJuZWxfdHlwZSA9ICJub3JtX2JveCIsCiAgICBoID0gSW5mLAogICAgUl9tYXggPSBJbmYsCiAgICBsaW5lY29sb3VyID0gImJyb3duIikKICAKICBub193IDwtIHJvdW5kKGxvZyhwJHdfaW5mIC8gcCR3X21pbikgLyBwJGR4KQogIAogIHBhcmFtcyA8LSBzZXRfbXVsdGlzcGVjaWVzX21vZGVsKAogICAgbm9fdyA9IG5vX3csCiAgICBzcGVjaWVzX3BhcmFtcywKICAgIGxhbWJkYSA9IHAkbGFtYmRhLAogICAga2FwcGEgPSBrYXBwYSwKICAgIHdfcHBfY3V0b2ZmID0gcCR3X3BwX2N1dG9mZiwKICAgIHEgPSBwJGFscGhhLAogICAgcGxhbmt0b25fZHluYW1pY3MgPSBwbGFua3Rvbl9sb2dpc3RpYykKCiAgcGFyYW1zQHJyX3BwW10gPC0gcCRyMCAqIHBhcmFtc0B3X2Z1bGxeKHAkcmhvIC0gMSkKICByZXR1cm4ocGFyYW1zKQp9CgpwYXJhbXMgPC0gc2V0TW9kZWwocCkKaSA8LSBwJGkwICogcGFyYW1zQHdfZnVsbF4oLXBhcmFtc0BsYW1iZGEpICogZXhwKC02LjkqKHBhcmFtc0BsYW1iZGEgLSAxKSkKYGBgCgojIFdpdGhvdXQgbGFydmFsIG1vcnRhbGl0eSBvciBjYW5uaWJhbGlzbQpXZSBmaXJzdCBydW4gdGhlIG1vZGVsIHdpdGhvdXQgbGFydmFsIG1vcnRhbGl0eSBhbmQgd2l0aG91dCBjYW5uaWJhbGlzbQpgYGB7cn0KcCRtdV9sIDwtIDAKcGFyYW1zIDwtIHNldEFuY2hvdnlNb3J0KHBhcmFtcywgcCkKcGFyYW1zQGludGVyYWN0aW9uW10gPC0gMApgYGAKCldlIHNldCBhbiBpbml0aWFsIGFidW5kYW5jZSBhbmQgcnVuIGZvciAxMCB5ZWFycy4KYGBge3J9CnBhcmFtc0Bpbml0aWFsX25bXSA8LSAwLjAwMSAqIHBhcmFtc0B3XigtMS44KQpwYXJhbXNAaW5pdGlhbF9uX3BwW10gPC0gcGFyYW1zQGNjX3BwCnNpbSA8LSBwcm9qZWN0KHBhcmFtcywgdF9tYXggPSAxMCwgZHQgPSBwJGR0LCBwcm9ncmVzc19iYXIgPSBGQUxTRSkKYGBgCgpBdCB0aGlzIHBvaW50IHdlIHJlZHVjZSB0aGUgYW5jaG92eSBhYnVuZGFuY2UgYnkgYW4gb3ZlcmFsbCBmYWN0b3Igb2YgMTBeNwphbmQgdGhlbiBydW4gdGhlIHNpbXVsYXRpb24gZm9yIGEgZnVydGhlciAzMCB5ZWFycy4KCmBgYHtyfQpzaW1AblsxMSwgLCBdIDwtIHNpbUBuWzExLCAsIF0gLyAxMF43CnNpbSA8LSBwcm9qZWN0KHNpbSwgdF9tYXggPSAzMCwgZHQgPSBwJGR0LCB0X3NhdmUgPSAwLjIsIHByb2dyZXNzX2JhciA9IEZBTFNFKQpgYGAKCiMjIEZpZ3VyZSAyYQpQbG90dGluZyB0aGUgc3BlY3RyYSBhdCB5ZWFyIDMwIGdpdmVzIEZpZ3VyZSAyYS4gSGVyZSB3ZSBwbG90IHRoZSAKcGxhbmt0b24gc3BlY3RydW0gYW5kIHRoZSBhbmNob3Z5IHNwZWN0cnVtIHVzaW5nIHRoZSBzYW1lIHktYXhpcy4KRmlndXJlIDJhIGluIHRoZSBwYXBlciB1c2VzIGRpZmZlcmVudCBheGVzLgpgYGB7cn0KcGxvdFNwZWN0cmEoc2ltLCBwb3dlciA9IDIsIHdsaW0gPSBjKDFlLTgsIE5BKSwgeWxpbSA9IGMoMWUtNSwgTkEpLAogICAgICAgICAgICB0aW1lX3JhbmdlID0gMzApCmBgYAoKVGhpcyBkb2VzIG5vdCBsb29rIGV4YWN0bHkgdGhlIHNhbWUgYXMgdGhlIGNvcnJlc3BvbmRpbmcgZ3JhcGggaW4gdGhlIHBhcGVyCmJlY2F1c2UgdGhlIHBpbGUtdXAgaXMgbm90IHNtb290aGVkIGJ5IGRpZmZ1c2lvbiwgYnV0IGl0IGRpc3BsYXlzIHRoZSBzYW1lIApxdWFsaXRhdGl2ZSBiZWhhdmlvdXIuCgoKCiMjIEZpZ3VyZSAyYgpGaWd1cmUgMmIgcGxvdHMgdGhlIGRlYXRoIHJhdGUgb24gdGhlIGFuY2hvdnkgYXMgYSBmdW5jdGlvbiBvZiBhbmNob3Z5IGJvZHkgCnNpemUuCmBgYHtyfQp0IDwtIGFzLm51bWVyaWMoZGltbmFtZXMoc2ltQG4pJHRpbWUpID09IDMwCm1vcnQgPC0gZ2V0TW9ydChwYXJhbXMsIG4gPSBzaW1Ablt0LCAsIF0sCiAgICAgICAgICAgICAgICBuX3BwID0gc2ltQG5fcHBbdCwgXSwgZWZmb3J0ID0gMCkKbW9ydCA8LSBtZWx0KG1vcnQpCnBsb3RfbHkobW9ydCkgJT4lIAogICAgYWRkX2xpbmVzKHggPSB+d19wcmV5LCB5ID0gfnZhbHVlKSAlPiUgCiAgICBsYXlvdXQocCwgeGF4aXMgPSBsaXN0KHR5cGUgPSAibG9nIiwgZXhwb25lbnRmb3JtYXQgPSAicG93ZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZV90ZXh0ID0gImJvZHkgbWFzcyAoZykiKSwKICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGVfdGV4dCA9ICJkZWF0aCByYXRlICgxL3llYXIpIikpCmBgYAoKCiMjIEZpZ3VyZSAyYwpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQphYm0gPC0gbWVsdChnZXRCaW9tYXNzKHNpbSkpCnBibSA8LSBzaW1Abl9wcCAlKiUgKHBhcmFtc0B3X2Z1bGwgKiBwYXJhbXNAZHdfZnVsbCkKcGJtIDwtIG1lbHQocGJtKQpwYm0kVmFyMiA8LSBOVUxMCnBibSRzcCA9ICJQbGFua3RvbiIKYm0gPC0gcmJpbmQocGJtLCBhYm0pCnBsb3RfbHkoYm0pICU+JSAKICAgIGZpbHRlcih0aW1lID49IDEwKSAlPiUgCiAgICBhZGRfbGluZXMoeCA9IH50aW1lLCB5ID0gfnZhbHVlLCBjb2xvciA9IH5zcCkgJT4lIAogICAgIyBVc2UgbG9nYXJpdGhtaWMgYXhlcwogICAgbGF5b3V0KHAsIHlheGlzID0gbGlzdCh0eXBlID0gImxvZyIsIGV4cG9uZW50Zm9ybWF0ID0gInBvd2VyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGVfdGV4dCA9ICJiaW9tYXNzIChnL21eMykiKSwKICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGVfdGV4dCA9ICJ0aW1lICh5ZWFyKSIpKQpgYGAKCgojIFdpdGggY2FubmliYWxpc20KVHVybiBvbiBjYW5uaWJhbGlzbQpgYGB7cn0KcGFyYW1zQGludGVyYWN0aW9uW10gPC0gMQpgYGAKCldlIHNldCBhbiBpbml0aWFsIGFidW5kYW5jZSBhbmQgcnVuIGZvciAxMCB5ZWFycy4KYGBge3J9CnBhcmFtc0Bpbml0aWFsX25bXSA8LSAwLjAwMSAqIHBhcmFtc0B3XigtMS44KQpwYXJhbXNAaW5pdGlhbF9uX3BwW10gPC0gcGFyYW1zQGNjX3BwCnNpbWMgPC0gcHJvamVjdChwYXJhbXMsIHRfbWF4ID0gMTAsIGR0ID0gcCRkdCwgcHJvZ3Jlc3NfYmFyID0gRkFMU0UpCmBgYAoKQXQgdGhpcyBwb2ludCB3ZSByZWR1Y2UgdGhlIGFuY2hvdnkgYWJ1bmRhbmNlIGJ5IGFuIG92ZXJhbGwgZmFjdG9yIG9mIDEwXjcKYW5kIHRoZW4gcnVuIHRoZSBzaW11bGF0aW9uIGZvciBhIGZ1cnRoZXIgMzAgeWVhcnMuCgpgYGB7cn0Kc2ltY0BuWzExLCAsIF0gPC0gc2ltY0BuWzExLCAsIF0gLyAxMF43CnNpbWMgPC0gcHJvamVjdChzaW1jLCB0X21heCA9IDMwLCBkdCA9IHAkZHQsIHRfc2F2ZSA9IDAuMiwgcHJvZ3Jlc3NfYmFyID0gRkFMU0UpCmBgYAoKCiMjIEZpZ3VyZSAyZApXaGlsZSBGaWd1cmUgMmQgc2hvd3MgdGhlIGJhY2tncm91bmQgZGVhdGggYW5kIHRoZSBsYXJ2YWwgZGVhdGggc2VwYXJhdGVseSwKaGVyZSBmb3Igc2ltcGxpY2l0eSB3ZSBwbG90IG9ubHkgdGhlaXIgc3VtLgpgYGB7cn0KdCA8LSBhcy5udW1lcmljKGRpbW5hbWVzKHNpbWNAbikkdGltZSkgPT0gMzYuOAptb3J0IDwtIGdldE1vcnQocGFyYW1zLCBuID0gc2ltY0BuW3QsICwgXSwKICAgICAgICAgICAgICAgIG5fcHAgPSBzaW1jQG5fcHBbdCwgXSwgZWZmb3J0ID0gMCkKbW9ydCA8LSBtZWx0KG1vcnQpCnBsb3RfbHkobW9ydCkgJT4lIAogICAgYWRkX2xpbmVzKHggPSB+d19wcmV5LCB5ID0gfnZhbHVlKSAlPiUgCiAgICBsYXlvdXQocCwgeGF4aXMgPSBsaXN0KHR5cGUgPSAibG9nIiwgZXhwb25lbnRmb3JtYXQgPSAicG93ZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZV90ZXh0ID0gImJvZHkgbWFzcyAoZykiKSwKICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGVfdGV4dCA9ICJkZWF0aCByYXRlICgxL3llYXIpIikpCmBgYAoKV2UgbWFkZSB0aGUgcGxvdCBmb3IgdGltZSA9IDM2LjggeWVhcnMgYmVjYXVzZSB0aGUgb3NjaWxsYXRpb25zIG9mIHRoZSBzcGVjdHJ1bQphcmUgc2hpZnRlZCB3aXRoIHJlc3BlY3QgdG8gdGhvc2UgaW4gdGhlIHBhcGVyLCBhcyB0aGUgZm9sbG93aW5nIGZpZ3VyZSBzaG93cy4KCiMjIEZpZ3VyZSAyZQpgYGB7cn0KYWJtIDwtIG1lbHQoZ2V0QmlvbWFzcyhzaW1jKSkKYWJtciA8LSBtZWx0KGdldEJpb21hc3Moc2ltYywgbWluX3cgPSAwLjAxLCBtYXhfdyA9IDAuNCkpCmFibXIkc3AgPSAic21hbGwgQW5jaG92eSIKcGJtIDwtIHNpbWNAbl9wcCAlKiUgKHBhcmFtc0B3X2Z1bGwgKiBwYXJhbXNAZHdfZnVsbCkKcGJtIDwtIG1lbHQocGJtKQpwYm0kVmFyMiA8LSBOVUxMCnBibSRzcCA9ICJQbGFua3RvbiIKYm0gPC0gcmJpbmQocGJtLCBhYm0sIGFibXIpCnBsb3RfbHkoYm0pICU+JSAKICAgIGZpbHRlcih0aW1lID49IDEwKSAlPiUgCiAgICBhZGRfbGluZXMoeCA9IH50aW1lLCB5ID0gfnZhbHVlLCBjb2xvciA9IH5zcCkgJT4lIAogICAgIyBVc2UgbG9nYXJpdGhtaWMgYXhlcwogICAgbGF5b3V0KHAsIHlheGlzID0gbGlzdCh0eXBlID0gImxvZyIsIGV4cG9uZW50Zm9ybWF0ID0gInBvd2VyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGVfdGV4dCA9ICJiaW9tYXNzIChnL21eMykiLAogICAgICAgICAgICAgICAgICAgICAgICAgICByYW5nZSA9IGMoLTcsIDIpKSwKICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGVfdGV4dCA9ICJ0aW1lICh5ZWFyKSIpKQpgYGAKCiMjIEFuaW1hdGlvbgpIZXJlIGlzIGFuIGFuaW1hdGlvbiBzaG93aW5nIHRoZSBldm9sdXRpb24gb2YgdGhlCnNwZWN0cmEgZnJvbSB5ZWFyIDI2IHRvIHllYXIgNDAuCmBgYHtyIHdhcm5pbmc9RkFMU0V9Cm5mIDwtIG1lbHQoc2ltY0BuKQpuX3BwZiA8LSBtZWx0KHNpbWNAbl9wcCkKbl9wcGYkc3AgPC0gIlBsYW5rdG9uIgpuZiA8LSByYmluZChuZiwgbl9wcGYpCgpwbG90X2x5KG5mKSAlPiUKICAgICMgc2hvdyBvbmx5IHBhcnQgb2YgcGxhbmt0b24gc3BlY3RydW0KICAgIGZpbHRlcih3ID4gMTBeLTUpICU+JSAKICAgICMgc3RhcnQgYXQgdGltZSAyMAogICAgZmlsdGVyKHRpbWUgPj0gMjYpICU+JSAKICAgICMgY2FsY3VsYXRlIGJpb21hc3MgZGVuc2l0eSB3aXRoIHJlc3BlY3QgdG8gbG9nIHNpemUKICAgIG11dGF0ZShiID0gdmFsdWUgKiB3XjIpICU+JSAKICAgICMgUGxvdCBsaW5lcwogICAgYWRkX2xpbmVzKAogICAgICAgIHggPSB+dywgeSA9IH5iLAogICAgICAgIGNvbG9yID0gfnNwLAogICAgICAgIGZyYW1lID0gfnRpbWUsCiAgICAgICAgbGluZSA9IGxpc3Qoc2ltcGxpZnkgPSBGQUxTRSkKICAgICkgJT4lIAogICAgIyBVc2UgbG9nYXJpdGhtaWMgYXhlcwogICAgbGF5b3V0KHAsIHhheGlzID0gbGlzdCh0eXBlID0gImxvZyIsIGV4cG9uZW50Zm9ybWF0ID0gInBvd2VyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGVfdGV4dCA9ICJib2R5IG1hc3MgKGcpIiksCiAgICAgICAgICAgeWF4aXMgPSBsaXN0KHR5cGUgPSAibG9nIiwgZXhwb25lbnRmb3JtYXQgPSAicG93ZXIiLAogICAgICAgICAgICAgICAgICAgICAgICB0aXRsZV90ZXh0ID0gImJpb21hc3MgKGcvbV4zKSIsCiAgICAgICAgICAgICAgICAgICAgICAgIHJhbmdlID0gYygtOCwgMCkpKQpgYGAKCiMgV2l0aCBsYXJ2YWwgbW9ydGFsaXR5ClR1cm4gb24gbGFydmFsIG1vcnRhbGl0eQpgYGB7cn0KcCRtdV9sIDwtIDIxCnBhcmFtcyA8LSBzZXRBbmNob3Z5TW9ydChwYXJhbXMsIHApCmBgYAoKV2Ugc2V0IGFuIGluaXRpYWwgYWJ1bmRhbmNlIGFuZCBydW4gZm9yIDEwIHllYXJzLgpgYGB7cn0KcGFyYW1zQGluaXRpYWxfbltdIDwtIDAuMDAxICogcGFyYW1zQHdeKC0xLjgpCnBhcmFtc0Bpbml0aWFsX25fcHBbXSA8LSBwYXJhbXNAY2NfcHAKc2ltbCA8LSBwcm9qZWN0KHBhcmFtcywgdF9tYXggPSAxMCwgZHQgPSBwJGR0LCBwcm9ncmVzc19iYXIgPSBGQUxTRSkKYGBgCgpBdCB0aGlzIHBvaW50IHdlIHJlZHVjZSB0aGUgYW5jaG92eSBhYnVuZGFuY2UgYnkgYW4gb3ZlcmFsbCBmYWN0b3Igb2YgMTBeNwphbmQgdGhlbiBydW4gdGhlIHNpbXVsYXRpb24gZm9yIGEgZnVydGhlciAzMCB5ZWFycy4KCmBgYHtyfQpzaW1sQG5bMTEsICwgXSA8LSBzaW1sQG5bMTEsICwgXSAvIDEwXjcKc2ltbCA8LSBwcm9qZWN0KHNpbWwsIHRfbWF4ID0gMzAsIGR0ID0gcCRkdCwgdF9zYXZlID0gMC4yLCBwcm9ncmVzc19iYXIgPSBGQUxTRSkKYGBgCgojIyBGaWd1cmUgMmYKSSBoYXZlIG5vdCB5ZXQgc3BsaXQgdGhlIG1vcnRhbGl0eSB1cCBpbnRvIGl0cyBjYXVzZXMgaW4gdGhlIGZvbGxvd2luZyBmaWd1cmUuIEJ1dCBvdmVyYWxsIGl0IGxvb2tzIHJpZ2h0LgpgYGB7cn0KdCA8LSBhcy5udW1lcmljKGRpbW5hbWVzKHNpbWxAbikkdGltZSkgPT0gMzAKbW9ydCA8LSBnZXRNb3J0KHBhcmFtcywgbiA9IHNpbWxAblt0LCAsIF0sCiAgICAgICAgICAgICAgICBuX3BwID0gc2ltbEBuX3BwW3QsIF0sIGVmZm9ydCA9IDApCm1vcnQgPC0gbWVsdChtb3J0KQpwbG90X2x5KG1vcnQpICU+JSAKICAgIGFkZF9saW5lcyh4ID0gfndfcHJleSwgeSA9IH52YWx1ZSkgJT4lIAogICAgbGF5b3V0KHAsIHhheGlzID0gbGlzdCh0eXBlID0gImxvZyIsIGV4cG9uZW50Zm9ybWF0ID0gInBvd2VyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGVfdGV4dCA9ICJib2R5IG1hc3MgKGcpIiksCiAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlX3RleHQgPSAiZGVhdGggcmF0ZSAoMS95ZWFyKSIpKQpgYGAKCiMjIEZpZ3VyZSAyZwpgYGB7ciB3YXJuaW5nPUZBTFNFfQphYm0gPC0gbWVsdChnZXRCaW9tYXNzKHNpbWwpKQpwYm0gPC0gc2ltbEBuX3BwICUqJSAocGFyYW1zQHdfZnVsbCAqIHBhcmFtc0Bkd19mdWxsKQpwYm0gPC0gbWVsdChwYm0pCnBibSRWYXIyIDwtIE5VTEwKcGJtJHNwID0gIlBsYW5rdG9uIgpibSA8LSByYmluZChhYm0sIHBibSkKcGxvdF9seShibSkgJT4lIAogICAgZmlsdGVyKHRpbWUgPj0gMTApICU+JSAKICAgIGFkZF9saW5lcyh4ID0gfnRpbWUsIHkgPSB+dmFsdWUsIGNvbG9yID0gfnNwKSAlPiUgCiAgICAjIFVzZSBsb2dhcml0aG1pYyBheGVzCiAgICBsYXlvdXQocCwgeWF4aXMgPSBsaXN0KHR5cGUgPSAibG9nIiwgZXhwb25lbnRmb3JtYXQgPSAicG93ZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZV90ZXh0ID0gImJpb21hc3MgKGcvbV4zKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmdlID0gYygtNywgMikpLAogICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZV90ZXh0ID0gInRpbWUgKHllYXIpIikpCmBgYAoKIyMgRmlndXJlIDNhCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpnY3AgPC0gcGxvdEdyb3d0aEN1cnZlcyhzaW1sLCBtYXhfYWdlID0gNCkKZ2NwICsgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gImxvZzEwIikKYGBgCgojIyBGaWd1cmUgM2IKCmBgYHtyfQp0X21pbl9pZHggPC0gc3VtKGFzLm51bWVyaWMoZGltbmFtZXMoc2ltbEBuKSR0aW1lKSA8PSAxNSkKdF9tYXhfaWR4IDwtIGRpbShzaW1sQG4pWzFdCnRfc3RlcF9pZHggPC0gMSAvIDAuMiAgIyAxIHllYXIgc3RlcHMKc3NiIDwtIGdldFNTQihzaW1sKVtzZXEodF9taW5faWR4LCB0X21heF9pZHggLSB0X3N0ZXBfaWR4LCB0X3N0ZXBfaWR4KV0KcmVjX2lkeCA8LSBzdW0ocGFyYW1zQHcgPCAxMCkKbl9yZWMgPC0gc2ltbEBuW3NlcSh0X21pbl9pZHggKyB0X3N0ZXBfaWR4LCB0X21heF9pZHgsIHRfc3RlcF9pZHgpLCAsIHJlY19pZHhdCiMgQ29udmVydCB0byBkZW5zaXR5IGluIGxvZyB3ZWlnaHQKbl9yZWMgPC0gbl9yZWMgKiBwYXJhbXNAd1tyZWNfaWR4XQpwbG90KHNzYiwgbl9yZWMsIHR5cGUgPSAibCIsIGxvZyA9ICJ4eSIsCiAgICAgeGxpbSA9IGMoMWUtNSwgMWUtMSksIHlsaW0gPSBjKDFlLTUsIDFlLTIpKQpgYGAKCgojIFJhbmRvbSBwbGFua3RvbgoKU3dpdGNoIG9uIHJhbmRvbW5lc3MgZm9yIHBsYW5rdG9uIGNhcnJ5aW5nIGNhcGFjaXR5CmBgYHtyfQpwbGFua3Rvbl9zdGF0ZSRyYW5kb20gPC0gVFJVRQpgYGAKT2YgY291cnNlIG91ciBmaWd1cmVzIHdpbGwgbm90IGxvb2sgZXhhY3RseSBsaWtlIHRob3NlIGluIHRoZSBwYXBlciBiZWNhdXNlIHdlCndpbGwgZ2V0IGEgZGlmZmVyZW50IHJhbmRvbWlzYXRpb24sIGJ1dCB0aGV5IHdpbGwgYmUgcXVhbGl0YXRpdmVseSB0aGUgc2FtZS4KCldlIHNldCBhbiBpbml0aWFsIGFidW5kYW5jZSBhbmQgcnVuIGZvciAxMCB5ZWFycy4KYGBge3J9CnBhcmFtc0Bpbml0aWFsX25bXSA8LSAwLjAwMSAqIHBhcmFtc0B3XigtMS44KQpwYXJhbXNAaW5pdGlhbF9uX3BwW10gPC0gcGFyYW1zQGNjX3BwCnNpbXIgPC0gcHJvamVjdChwYXJhbXMsIHRfbWF4ID0gMTAsIGR0ID0gcCRkdCwgcHJvZ3Jlc3NfYmFyID0gRkFMU0UpCmBgYAoKQXQgdGhpcyBwb2ludCB3ZSByZWR1Y2UgdGhlIGFuY2hvdnkgYWJ1bmRhbmNlIGJ5IGFuIG92ZXJhbGwgZmFjdG9yIG9mIDEwXjcKYW5kIHRoZW4gcnVuIHRoZSBzaW11bGF0aW9uIGZvciBhIGZ1cnRoZXIgMzAgeWVhcnMuCgpgYGB7cn0Kc2ltckBuWzExLCAsIF0gPC0gc2ltckBuWzExLCAsIF0gLyAxMF43CnNpbXIgPC0gcHJvamVjdChzaW1yLCB0X21heCA9IDMwLCBkdCA9IHAkZHQsIHRfc2F2ZSA9IDAuMiwgcHJvZ3Jlc3NfYmFyID0gRkFMU0UpCmBgYAoKIyMgRmlndXJlIDRhCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmFibSA8LSBtZWx0KGdldEJpb21hc3Moc2ltcikpCnBibSA8LSBzaW1yQG5fcHAgJSolIChwYXJhbXNAd19mdWxsICogcGFyYW1zQGR3X2Z1bGwpCnBibSA8LSBtZWx0KHBibSkKcGJtJFZhcjIgPC0gTlVMTApwYm0kc3AgPSAiUGxhbmt0b24iCmJtIDwtIHJiaW5kKGFibSwgcGJtKQpwbG90X2x5KGJtKSAlPiUgCiAgICBmaWx0ZXIodGltZSA+PSAxMCkgJT4lIAogICAgYWRkX2xpbmVzKHggPSB+dGltZSwgeSA9IH52YWx1ZSwgY29sb3IgPSB+c3ApICU+JSAKICAgICMgVXNlIGxvZ2FyaXRobWljIGF4ZXMKICAgIGxheW91dChwLCB5YXhpcyA9IGxpc3QodHlwZSA9ICJsb2ciLCBleHBvbmVudGZvcm1hdCA9ICJwb3dlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlX3RleHQgPSAiYmlvbWFzcyAoZy9tXjMpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZ2UgPSBjKC0zLjIsIDAuOCkpLAogICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZV90ZXh0ID0gInRpbWUgKHllYXIpIikpCmBgYAoKIyMgRmlndXJlIDRiCgpgYGB7cn0KdF9taW5faWR4IDwtIHN1bShhcy5udW1lcmljKGRpbW5hbWVzKHNpbWxAbikkdGltZSkgPD0gMTUpCnRfbWF4X2lkeCA8LSBkaW0oc2ltbEBuKVsxXQp0X3N0ZXBfaWR4IDwtIDEgLyAwLjIgICMgMSB5ZWFyIHN0ZXBzCnNzYiA8LSBnZXRTU0Ioc2ltcilbc2VxKHRfbWluX2lkeCwgdF9tYXhfaWR4IC0gdF9zdGVwX2lkeCwgdF9zdGVwX2lkeCldCnJlY19pZHggPC0gc3VtKHBhcmFtc0B3IDwgMTApCm5fcmVjIDwtIHNpbXJAbltzZXEodF9taW5faWR4ICsgdF9zdGVwX2lkeCwgdF9tYXhfaWR4LCB0X3N0ZXBfaWR4KSwgLCByZWNfaWR4XQojIENvbnZlcnQgdG8gZGVuc2l0eSBpbiBsb2cgd2VpZ2h0Cm5fcmVjIDwtIG5fcmVjICogcGFyYW1zQHdbcmVjX2lkeF0KcGxvdChzc2IsIG5fcmVjLCBsb2cgPSAieHkiLCB5bGltID0gYygxZS01LCAxZS0xKSwgcGNoID0gMjAsCiAgICAgeGxhYiA9ICJzcGF3bmluZyBzdG9jayBiaW9tYXNzIChnL21eMykiLCB4bGltID0gYygxZS0zLCAxKSwKICAgICB5bGFiID0gImRlbnNpdHkgb2YgcmVjcnVpdHMgKDEvbV4zKSIpCmBgYA==